package com.xiongtian.springframework.aop.framework.autoproxy;

import com.xiongtian.springframework.aop.*;
import com.xiongtian.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor;
import com.xiongtian.springframework.aop.framework.ProxyFactory;
import com.xiongtian.springframework.beans.BeansException;
import com.xiongtian.springframework.beans.PropertyValues;
import com.xiongtian.springframework.beans.factory.BeanFactory;
import com.xiongtian.springframework.beans.factory.BeanFactoryAware;
import com.xiongtian.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import com.xiongtian.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
 * @Author: xiongtian
 * @CreateTime: 2025-04-18  16:22
 * @Version: 1.0
 * @Description: 默认的切面自动代理创建器
 */

public class DefaultAdvisorAutoProxyCreator implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {

    private DefaultListableBeanFactory beanFactory;

    /**
     * 存放代理对象的名称
     * 防止重复代理
     * 因为代理对象是在实例化之后生成的，所以需要在实例化之后判断是否需要生成代理对象
     * 如果需要生成代理对象，那么就需要将代理对象的名称放入到该集合中，避免重复代理
     * 如果不需要生成代理对象，那么就不需要将代理对象的名称放入到该集合中，避免重复代理
     */
    private final Set<Object> earlyProxyReferences = Collections.synchronizedSet(new HashSet<Object>());

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = (DefaultListableBeanFactory) beanFactory;
    }


    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        return null;
    }

    /**
     * 实例化之前生成代理对象
     *
     * @param beanName the name of the bean
     * @return the bean instance to use, or <code>null</code> to proceed with the default instantiation
     * @throws BeansException in case of errors
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (!earlyProxyReferences.contains(beanName)) {
            return wrapIfNecessary(bean, beanName);
        }
        return bean;
    }


    /**
     * 判断是否是基础设施类
     *
     * @param beanClass the class of the bean
     * @return whether the bean is an infrastructure class
     */
    private boolean isInfrastructureClass(Class<?> beanClass) {
        return Advice.class.isAssignableFrom(beanClass) || Pointcut.class.isAssignableFrom(beanClass) || Advisor.class.isAssignableFrom(beanClass);
    }

    /**
     * 实例化之后生成代理对象
     *
     * @param bean     bean对象
     * @param beanName bean名称
     * @return bean对象
     * @throws BeansException 异常
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }


    @Override
    public PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        return pvs;
    }


    /**
     * 获取早期的bean引用
     *
     * @param bean     bean对象
     * @param beanName bean名称
     * @return bean对象
     */
    @Override
    public Object getEarlyBeanReference(Object bean, String beanName) {
        earlyProxyReferences.add(beanName);
        return wrapIfNecessary(bean, beanName);
    }


    /**
     * 包装对象
     * 如果是基础设施类，那么就不需要包装
     * 如果不是基础设施类，那么就需要包装
     * 包装的方式是：
     * 1. 获取所有的切面
     * 2. 遍历切面，判断是否匹配
     * 3. 如果匹配，那么就需要包装
     * 4. 如果不匹配，那么就不需要包装
     * 5. 如果匹配，那么就需要包装
     * 6. 如果不匹配，那么就不需要包装
     *
     * @param bean     bean对象
     * @param beanName bean名称
     * @return bean对象
     */
    protected Object wrapIfNecessary(Object bean, String beanName) {
        if (isInfrastructureClass(bean.getClass())) return bean;

        Collection<AspectJExpressionPointcutAdvisor> advisors = beanFactory.getBeansOfType(AspectJExpressionPointcutAdvisor.class).values();

        for (AspectJExpressionPointcutAdvisor advisor : advisors) {
            ClassFilter classFilter = advisor.getPointcut().getClassFilter();
            // 过滤匹配类
            if (!classFilter.matches(bean.getClass())) continue;

            AdvisedSupport advisedSupport = new AdvisedSupport();

            TargetSource targetSource = new TargetSource(bean);
            advisedSupport.setTargetSource(targetSource);
            advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice());
            advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher());
            advisedSupport.setProxyTargetClass(true);

            // 返回代理对象
            return new ProxyFactory(advisedSupport).getProxy();
        }
        return bean;
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        return true;
    }
}
